using Client;
using Greet;
using Grpc.Core;
using Grpc.Core.Interceptors;
using Grpc.Net.Client;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

var loggerFactory = LoggerFactory.Create(b => b.AddConsole());
using var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
    LoggerFactory = loggerFactory
});
var invoker = channel.Intercept(new ClientLoggerInterceptor(loggerFactory));

var client = new Greeter.GreeterClient(invoker);

BlockingUnaryCallExample(client);

await UnaryCallExample(client);

await ServerStreamingCallExample(client);

await ClientStreamingCallExample(client);

await BidirectionalCallExample(client);

Console.WriteLine("Shutting down");
Console.WriteLine("Press any key to exit...");
Console.ReadKey();

static void BlockingUnaryCallExample(Greeter.GreeterClient client)
{
    var reply = client.SayHello(new HelloRequest { Name = "GreeterClient" });
    Console.WriteLine("Greeting: " + reply.Message);
}

static async Task UnaryCallExample(Greeter.GreeterClient client)
{
    var reply = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });
    Console.WriteLine("Greeting: " + reply.Message);
}

static async Task ServerStreamingCallExample(Greeter.GreeterClient client)
{
    var cts = new CancellationTokenSource();
    cts.CancelAfter(TimeSpan.FromSeconds(3.5));

    using var call = client.SayHellos(new HelloRequest { Name = "GreeterClient" }, cancellationToken: cts.Token);
    try
    {
        await foreach (var message in call.ResponseStream.ReadAllAsync())
        {
            Console.WriteLine("Greeting: " + message.Message);
        }
    }
    catch (RpcException ex) when (ex.StatusCode == StatusCode.Cancelled)
    {
        Console.WriteLine("Stream cancelled.");
    }
}

static async Task ClientStreamingCallExample(Greeter.GreeterClient client)
{
    using var call = client.SayHelloToLotsOfBuddies();
    for (var i = 0; i < 3; i++)
    {
        await call.RequestStream.WriteAsync(new HelloRequest { Name = $"GreeterClient{i + 1}" });
    }

    await call.RequestStream.CompleteAsync();
    var reply = await call;
    Console.WriteLine("Greeting: " + reply.Message);
}

static async Task BidirectionalCallExample(Greeter.GreeterClient client)
{
    using var call = client.SayHellosToLotsOfBuddies();
    var readTask = Task.Run(async () =>
    {
        await foreach (var message in call.ResponseStream.ReadAllAsync())
        {
            Console.WriteLine("Greeting: " + message.Message);
        }
    });

    for (var i = 0; i < 3; i++)
    {
        await call.RequestStream.WriteAsync(new HelloRequest { Name = $"GreeterClient{i + 1}" });
    }

    await call.RequestStream.CompleteAsync();
    await readTask;
}
